﻿var Template = Template || {};
/**
 * 模板处理
 * 设计：王集鹄(zswang)
 * 日期：2010.12.26
 */

(function() {
	var htmlDecodeDict = { "quot": '"', "lt": "<", "gt": ">", "amp": "&", "nbsp": " " },
		htmlEncodeDict = { '"': "quot", "<": "lt", ">": "gt", "&": "amp", " ": "nbsp" },
		readerCaches = {}, // 模板解析函数缓存
		dataCaches = {}, // 数据缓存, key:请求的链接
		waitRequest = {}; // 正在等待的请求
	function htmlDecode(html) {
		return (html||"").replace(/&(quot|lt|gt|amp|nbsp);/ig, function(all, key) {
			return htmlDecodeDict[key];
		}).replace(/&#u([a-f\d]{4});/ig, function(all, hex) {
			return String.fromCharCode(parseInt("0x" + hex));
		}).replace(/&#(\d+);/ig, function(all, number) {
			return String.fromCharCode(+number);
		});
	}
	function htmlEncode(html) {
		return (html||"").replace(/["<>& ]/g, function(all) {
			return "&" + htmlEncodeDict[all] + ";";
		}).replace(/[^\x00-\xff]/g, function(all) {
			return "&#" + all.charCodeAt(0) + ";";
		});
	}
	function request(url, type, data, loaded, error) {
		var xmlhttp = window.XMLHttpRequest ? new XMLHttpRequest() :
			new ActiveXObject("Microsoft.XMLHTTP"), timer = setTimeout(function() {
				xmlhttp.abort();
				if (error) error(xmlhttp, true);
			}, 3000);
		
		xmlhttp.onreadystatechange = function() {
			if (xmlhttp.readyState == 4) {
				clearTimeout(timer);
				if (xmlhttp.status == 200 || xmlhttp.status == 0)
					loaded(xmlhttp);
				else if (parseFloat(xmlhttp.status) > 300 && error)
					error(xmlhttp);
			}
		}
		xmlhttp.open(type || "GET", url, true);
		if (data) {
			xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); 
			xmlhttp.setRequestHeader("Content-Length", String(data).length);
		}
		xmlhttp.send(data);
	}
	/**
	 * 格式化函数
	 * @param {String} template 模板
	 * @param {Object} json 数据项
	 */
	function format(template, json) {
		return template.replace(/(\$|~|#|@)\{(.*?)\}/g, function(all, flag, key) {
			var text = json && (key in json) ? json[key] : "";
			switch (flag) {
				case "~": return text.replace(/["'\\]/g, "\\$&").replace(/\n/g, "\\n");
				case "#": return htmlEncode(text);
				case "@": return encodeURIComponent(text);
				case "$": return text;
			}
		});
	}
	/**
	 * 安全值处理，避免访问unidefined抛出异常
	 * @param {String} expression 表达式
	 */
	function safeValue(expression) {
		if (!expression) return "";
		var match = expression.match(/^[^'"a-zA-Z\d_$]*([a-zA-Z\d_$]+)/);
		if (!match) return expression;
		return format('typeof ${1}=="undefined"?null:${0}', [expression, match[1]]);
	}
	/**
	 * 构造模板的处理函数
	 * @param {String} template 模板字符
	 */
	function analyse(template) {
		var guid = 0, body = ['with(_json_){_output_.push("', template
			.replace(/\r?\n/g, "\n") // 统一换行字符
			.replace(/[ \f\t\v]*(\$\{\s*((["'])[^"']+\3)\s*,\s*(.+?)\s*\})[ \f\t\v]*/g, "$1") // 去掉语句后面的换行
			.replace(/\s*(\{(([^"'{}]|"([^"]|\\[\s\S])*"|'([^']|\\[\s\S])*')*)\})[ \f\t\v]*/g, "$1") // 去掉语句后面的换行
			.replace(/["'\\]/g, "\\$&") // 处理转义符
			.replace(/\r|\n/g, "\\n") // 处理回车转义符
			.replace(/(@|~|#|\$)?\{(([^"'{}]|"([^"]|\\[\s\S])*"|'([^']|\\[\s\S])*')*)\}/g, '");$&_output_.push("') // 打印原文
			.replace(/((?:@|~|#|\$)?)\{(([^"'{}]|"([^"]|\\[\s\S])*"|'([^']|\\[\s\S])*')*)\}/g, function(all, flag, expression) { // 处理语句
				expression = expression
					.replace(/\\([\\'"])/g, "$1") // 还原转义
					.replace(/\belseif\b/g, "else if"); 
				if (flag) {
					switch (flag) {
						case "$": 
							var match = expression.match(/^\s*((["'])[^"']+\2)\s*,\s*(.+?)\s*$/) // 处理模板嵌套调用
							if (match)
								return format('var _t${0}_=[];_output_.push(_t${0}_);_output_.push(_process_(_t${0}_,${1},${2}));', [++guid, match[1], safeValue(match[3])]);
							return format('_output_.push(String(${0}));', [safeValue(expression)]); // 原样输出
						default: return format('_output_.push(_format_("${0}{0}",[${1}]));', [flag, safeValue(expression)]); // 格式化输出
					}
				}
				var prefix = "", suffix = "", match = expression.match(/^\s*foreach\s*\(\s*var\s+([a-zA-Z\d$_]+)\s+in\s+([\s\S]+?)\s*\)\s*$/);
				if (match) {
					return format('var _d${0}_=${2};for(var _i${0}_=0;_d${0}_&&_i${0}_<_d${0}_.length;_i${0}_++){var ${1}=_d${0}_[_i${0}_];', 
						[++guid, match[1], safeValue(match[2])]);
				}
				if (/^s*\/(if|while|for|foreach|switch)?\s*$/.test(expression)) {
					expression = "}";
				}
				if (/^\s*var\s+|^\s*(break|continue)\s*$/.test(expression)) {
					suffix = ";";
				}
				if (/^s*(if|while|for|else|switch)\b/.test(expression)) {
					suffix = "{";
				}
				if (/^\s*else\b/.test(expression)) {
					prefix = "}";
				}
				if (/^\s*(case|default)\b/.test(expression)) {
					suffix = ":";
				}
				return [prefix, expression, suffix].join("");
			}),
			'"); }'].join("").replace(/_output_\.push\(""\);/g, "");
		window.console && console.log(body);
		return new Function("_output_", "_json_", "_format_", "_process_", body);
	}
	function outputText(output) {
		var result = [];
		for (var i = 0; i < output.length; i++) {
			var item = output[i];
			if (item instanceof Array)
				result.push(outputText(item));
			else result.push(item);
		}
		return result.join("");
	}
	function build(name, json, callback) {
		if (!name || !json || !callback) return;
		var runtime = {
			output: [],
			request: 0, 
			complete: 0,
			callback: callback
		};

		function complete(url, reader, data) { // 请求完成
			dataCaches[url].status = "loaded";
			dataCaches[url].data = data;
			var waitOutput = dataCaches[url].waitOutput, i = waitOutput.length;
			while (i--) {
				reader(waitOutput[i], data, format, process);
				window.console && console.log(waitOutput[i]);
			} 
			runtime.complete++;
			if (runtime.request <= runtime.complete && runtime.waitComplete) {
				runtime.callback(outputText(runtime.output));
			}
		}
		/**
		 * 通过模板生成文本
		 * @param {String} name 模板名
		 * @param {Object} json 数据项
		 */
		function process(output, name, json) {
			if (!name || !json) return "";
			var reader = readerCaches[name], template;
			if (!reader) {
				var element = document.getElementById(name);
				if (!element) return "";
				if (/^(input|textarea)$/i.test(element.tagName))
					template = element.value;
				else template = htmlDecode(element.innerHTML);
				reader = readerCaches[name] = analyse(template);
				
			}
			window.console && console.log(String(reader));
			try {
				switch (typeof json) {
					case "string": 
						var items = [];
						output.push(items); // 插入一个待读项
						if (dataCaches[json]) { // 已经处理过
							switch(dataCaches[json].status) {
								case "loaded": // 已经加载过的直接填充
									reader(items, dataCaches[json].data, format, process);
									break;
								case "loading": // 正在加载中
									dataCaches[json].waitOutput.push(items);
									break; 
							}
							return;
						}
						dataCaches[json] = { status: "loading", waitOutput: [items] };
						waitRequest[json] = true;
						runtime.request++;
						request(json, "GET", null, function(xmlhttp) {
							try {
								complete(json, reader, eval("(" + xmlhttp.responseText + ")"), runtime);
							} catch(ex) {
								complete(json, reader, { error: true }, runtime);
							}
						}, function(xmlhttp, overtime) {
							complete(json, reader, { error: true }, runtime);
						});
						break;
					case "object":
						reader(output, json, format, process, runtime);
						break;
				}
			} catch(ex) {
				window.console && console.log(ex);
			}
		}
		process(runtime.output, name, json, runtime);
		if (runtime.request <= runtime.complete) {
			runtime.callback(outputText(runtime.output));
		} else {
			runtime.waitComplete = true;
		}
	}
	Template.build = build;
})();